package net.risesoft.service.identity.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

import net.risesoft.consts.CacheNameConsts;
import net.risesoft.entity.Y9Position;
import net.risesoft.entity.identity.Y9IdentityToResourceAndAuthorityBase;
import net.risesoft.entity.identity.position.Y9PositionToResourceAndAuthority;
import net.risesoft.entity.permission.Y9Authorization;
import net.risesoft.enums.ResourceTypeEnum;
import net.risesoft.manager.Y9OrgBaseManager;
import net.risesoft.manager.Y9PositionToResourceAndAuthorityManager;
import net.risesoft.repository.identity.position.Y9PositionToResourceAndAuthorityRepository;
import net.risesoft.service.identity.Y9PositionToResourceAndAuthorityService;
import net.risesoft.y9public.entity.resource.Y9ResourceBase;

/**
 * PositionToResourceAndAuthorityServiceImpl
 *
 * @author shidaobang
 * @author mengjuhua
 * @date 2022/4/6
 */
@Transactional(value = "rsTenantTransactionManager", readOnly = true)
@CacheConfig(cacheNames = CacheNameConsts.POSITIONS_TO_RESOURCES)
@Service
@RequiredArgsConstructor
public class Y9PositionToResourceAndAuthorityServiceImpl implements Y9PositionToResourceAndAuthorityService {
    
    private final Y9PositionToResourceAndAuthorityRepository y9PositionToResourceAndAuthorityRepository;

    private final Y9OrgBaseManager y9OrgBaseManager;
    private final Y9PositionToResourceAndAuthorityManager y9PositionToResourceAndAuthorityManager;
    
    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    @Transactional(readOnly = false)
    @Override
    public void deleteByAppId(String appId) {
        y9PositionToResourceAndAuthorityRepository.deleteByAppId(appId);
    }

    @Transactional(readOnly = false)
    @Override
    public void deleteByAuthorizationId(String authorizationId) {
        y9PositionToResourceAndAuthorityManager.deleteByAuthorizationId(authorizationId);
    }

    @Transactional(readOnly = false)
    @Override
    public void deleteByAuthorizationIdAndOrgUnitId(String authorizationId, String orgId) {
        List<Y9Position> allPersons = y9OrgBaseManager.listAllPositionsRecursionDownward(orgId);
        for (Y9Position y9Position : allPersons) {
            deleteByAuthorizationIdAndPositionId(authorizationId, y9Position.getId());
        }
    }

    @Transactional(readOnly = false)
    @Override
    public void deleteByAuthorizationIdAndPositionId(String authorizationId, String positionId) {
        y9PositionToResourceAndAuthorityRepository.deleteByAuthorizationIdAndPositionId(authorizationId, positionId);
    }

    @Transactional(readOnly = false)
    @Override
    public void deleteByOrgUnitId(String orgUnitId) {
        List<Y9Position> positionList = y9OrgBaseManager.listAllPositionsRecursionDownward(orgUnitId);
        for (Y9Position y9Position : positionList) {
            deleteByPositionId(y9Position.getId());
        }
    }

    @Transactional(readOnly = false)
    @Override
    public void deleteByPositionId(String positionId) {
        y9PositionToResourceAndAuthorityRepository.deleteByPositionId(positionId);
    }

    @Transactional(readOnly = false)
    @Override
    public void deleteByResourceId(String resourceId) {
        y9PositionToResourceAndAuthorityRepository.deleteByResourceId(resourceId);
    }

    @Override
    public boolean hasPermission(String positionId, String resourceId, Integer authority) {
        return !y9PositionToResourceAndAuthorityRepository.findByPositionIdAndResourceIdAndAuthority(positionId, resourceId, authority).isEmpty();
    }

    @Override
    public boolean hasPermissionByCustomId(String positionId, String customId, Integer authority) {
        return !y9PositionToResourceAndAuthorityRepository.findByPositionIdAndResourceCustomIdAndAuthority(positionId, customId, authority).isEmpty();
    }

    @Override
    public List<Y9PositionToResourceAndAuthority> list(String positionId, String parentResourceId, Integer authority) {
        if (StringUtils.isBlank(parentResourceId)) {
            List<Y9PositionToResourceAndAuthority> list = new ArrayList<>();
            list.addAll(y9PositionToResourceAndAuthorityRepository.findByPositionIdAndParentResourceIdIsNullAndAuthorityOrderByResourceTabIndex(positionId, authority));
            list.addAll(y9PositionToResourceAndAuthorityRepository.findByPositionIdAndParentResourceIdAndAuthorityOrderByResourceTabIndex(positionId, "", authority));
            return list;
        }
        return y9PositionToResourceAndAuthorityRepository.findByPositionIdAndParentResourceIdAndAuthorityOrderByResourceTabIndex(positionId, parentResourceId, authority);
    }

    @Override
    public List<Y9PositionToResourceAndAuthority> list(String positionId, String parentResourceId, Integer resourceType, Integer authority) {
        return y9PositionToResourceAndAuthorityRepository.findByPositionIdAndParentResourceIdAndAuthorityAndResourceTypeOrderByResourceTabIndex(positionId, parentResourceId, authority, resourceType);
    }

    @Override
    public List<Y9PositionToResourceAndAuthority> listAppsByAuthority(String positionId, Integer authority, boolean distinctResourceId) {
        List<Y9PositionToResourceAndAuthority> list = y9PositionToResourceAndAuthorityRepository.findByPositionIdAndAuthorityAndResourceTypeOrderByResourceTabIndex(positionId, authority, ResourceTypeEnum.APP.getValue());
        if (distinctResourceId) {
            list = list.stream().filter(distinctByKey(Y9IdentityToResourceAndAuthorityBase::getResourceId)).collect(Collectors.toList());
        }
        return list;
    }

    @Override
    public List<Y9PositionToResourceAndAuthority> listByPositionId(String positionId) {
        return y9PositionToResourceAndAuthorityRepository.findByPositionId(positionId);
    }

    @Transactional(readOnly = false)
    @Override
    public void saveOrUpdate(Y9ResourceBase y9ResourceBase, Y9Position y9Position, Y9Authorization y9Authorization, Boolean inherit) {
        y9PositionToResourceAndAuthorityManager.saveOrUpdate(y9ResourceBase, y9Position, y9Authorization, inherit);
    }

    @Transactional(readOnly = false)
    @Override
    public void updateByResourceId(String resourceId, String resourceName, String systemName, String systemCnName, String description) {
        List<Y9PositionToResourceAndAuthority> list = y9PositionToResourceAndAuthorityRepository.findByResourceId(resourceId);
        for (Y9PositionToResourceAndAuthority y9PositionToResourceAndAuthority : list) {
            y9PositionToResourceAndAuthority.setResourceName(resourceName);
            y9PositionToResourceAndAuthority.setSystemName(systemName);
            y9PositionToResourceAndAuthority.setSystemCnName(systemCnName);
            y9PositionToResourceAndAuthority.setResourceDescription(description);
            y9PositionToResourceAndAuthorityRepository.save(y9PositionToResourceAndAuthority);
        }
    }
}
